/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.opengoofy.index12306.framework.starter.designpattern.strategy;



import org.opengoofy.index12306.framework.starter.bases.ApplicationContextHolder;
import org.opengoofy.index12306.framework.starter.bases.init.ApplicationInitializingEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;

/**
 * 策略选择器抽象类
 * 实现了 ApplicationListener 接口，用于在应用程序初始化时加载所有策略
 */
public class AbstractStrategyChoose implements ApplicationListener<ApplicationInitializingEvent> {

    /**
     * 存储所有策略的集合
     * Key: 策略标识
     * Value: 具体策略实现类
     */
    private final Map<String, AbstractExecuteStrategy> abstractExecuteStrategyMap = new HashMap<>();

    /**
     * 根据策略标识选择具体的策略实现
     *
     * @param mark 策略标识
     * @param predicateFlag 是否启用正则表达式匹配
     *                     为 true 时使用正则表达式匹配策略
     *                     为 false 时使用精确匹配策略
     * @return 返回匹配到的策略实现类
     * @throws RuntimeException 当找不到匹配的策略时抛出异常
     */
    public AbstractExecuteStrategy choose(String mark, Boolean predicateFlag) {
        if (predicateFlag != null && predicateFlag) {
            // 使用正则表达式匹配策略
            return abstractExecuteStrategyMap.values().stream()
                    .filter(each -> StringUtils.hasText(each.patternMatchMark()))
                    .filter(each -> Pattern.compile(each.patternMatchMark()).matcher(mark).matches())
                    .findFirst()
                    .orElseThrow(() -> new RuntimeException("策略未定义"));
        }
        // 使用精确匹配策略
        return Optional.ofNullable(abstractExecuteStrategyMap.get(mark))
                .orElseThrow(() -> new RuntimeException(String.format("[%s] 策略未定义", mark)));
    }

    /**
     * 选择策略并执行，无返回结果
     *
     * @param mark 策略标识
     * @param requestParam 策略执行需要的参数
     * @param <REQUEST> 参数的类型
     */
    public <REQUEST> void chooseAndExecute(String mark, REQUEST requestParam) {
        AbstractExecuteStrategy executeStrategy = choose(mark, null);
        executeStrategy.execute(requestParam);
    }

    /**
     * 选择策略并执行，支持正则匹配，无返回结果
     *
     * @param mark 策略标识
     * @param requestParam 策略执行需要的参数
     * @param predicateFlag 是否启用正则表达式匹配
     * @param <REQUEST> 参数的类型
     */
    public <REQUEST> void chooseAndExecute(String mark, REQUEST requestParam, Boolean predicateFlag) {
        AbstractExecuteStrategy executeStrategy = choose(mark, predicateFlag);
        executeStrategy.execute(requestParam);
    }

    /**
     * 选择策略并执行，有返回结果
     *
     * @param mark 策略标识
     * @param requestParam 策略执行需要的参数
     * @param <REQUEST> 参数的类型
     * @param <RESPONSE> 返回结果的类型
     * @return 策略执行的结果
     */
    public <REQUEST, RESPONSE> RESPONSE chooseAndExecuteResp(String mark, REQUEST requestParam) {
        AbstractExecuteStrategy executeStrategy = choose(mark, null);
        return (RESPONSE) executeStrategy.executeResp(requestParam);
    }

    /**
     * 应用程序初始化时的回调方法
     * 用于加载所有策略实现类并检查是否有重复的策略标识
     *
     * @param event 应用程序初始化事件
     * @throws RuntimeException 当发现重复的策略标识时抛出异常
     */
    @Override
    public void onApplicationEvent(ApplicationInitializingEvent event) {
        // 获取所有策略实现类
        Map<String, AbstractExecuteStrategy> actual = ApplicationContextHolder.getBeansOfType(AbstractExecuteStrategy.class);
        // 遍历所有策略实现类并存入 Map
        actual.forEach((beanName, bean) -> {
            AbstractExecuteStrategy beanExist = abstractExecuteStrategyMap.get(bean.mark());
            if (beanExist != null) {
                throw new RuntimeException(String.format("[%s] Duplicate execution policy", bean.mark()));
            }
            abstractExecuteStrategyMap.put(bean.mark(), bean);
        });
    }
}
